
/**
 * 							MMU MEMORY MANAGEMENT
 * @brief	virtual memory management
 * @author	chy.
 * @note	this file manage the virtual memory and physical memory!
 * @comment	ARM MMU support many modes.we use 4KB per page.
 */

/**
 */

#ifndef	_K_MEM_SYS_MMU_H_
#define _K_MEM_SYS_MMU_H_

#include "mem_basedefine.h"
#include "k_mem_physical.h"
#include "../task/kernel_task_def.h"

/********************************************************************************
 * 										defines									*
 ********************************************************************************/
/**
 * comments :
 * 		ARM small page MPT informations:
 * 	12 8 12
 * 		about more information,please see ARM archive information.
 */


/**
 * NOTES AND COMMENTS:
 * 		we should define Domain Values as we will use DOAMIN to check permissions!
 * 	So, let's make assuming:
 * 		set CP15 domain register 0th field:	00			note: both system and user can't access these page(s).
 * 		set CP15 domain register 1st field: 01			note: can access but need permission check!(we always use this)
 * 		set CP15 domain register 2nd field: 11			note: both system and user can access these page
 * 															and about the permission check!
 *  So, the CP15 domain register value should be 0x00000034 !
 */

// domain defines
#define	K_MEM_MMU_DOMAIN_SYSTEM			0x01
#define	K_MEM_MMU_DOMAIN_USER			0x02

// permissions defines
#define	K_MEM_MMU_AP_SYSTEM				0x01					// system R/W ,user no permission
#define	K_MEM_MMU_AP_USER_READONLY		0x02					// system R/W ,user read-only
#define	K_MEM_MMU_AP_USER_READWRITE		0x03					// system R/W ,user R/W


/** 系统空间的页面的物理地址和虚拟地址的相互转换,仅限于系统空间内部的内存可用! */
#define	K_MEM_MMU_SYS_PHY2VIR(__phy_addr__)		((__phy_addr__) - (g_s_k_mem_phy_pagestart << 12))		// 物理地址转换为虚拟地址
#define K_MEM_MMU_SYS_VIR2PHY(__vir_addr__)		((__vir_addr__) + (g_s_k_mem_phy_pagestart << 12))		// 虚拟地址转换为物理地址

/**
 * COMMENTS:	First Level Page Descriptor
 * 		Coarse
 * 		[31:10] These bits form the corresponding bits of the physical
                address.
		[9]     Should Be Zero.
		[8:5]   Domain control bits.
		[4]     Must be 1.
		[3:2]   Should Be Zero.
		[1:0]   These bits indicate the page size and validity and are
                  interpreted as shown in next table.
 */
/**
 * COMMENTS:	Page Size and Validity
 * 		00 Invalid           Generates a section translation fault
 *		01 Coarse page table Indicates that this is a coarse page table descriptor
 *
 */

/**
 * 填充第一级页表项
 * 		__firstleveltableaddress__ 必须是虚拟地址
 * memode 为domain mode!
 */
#define	K_MEM_MMU_FILL_FMPT(__phy_addr__, __vir_addr__, __memmode__, __firstleveltable_vir_address__)	\
{	\
	unsigned int *__m_baseaddress__ = (unsigned int *)__firstleveltable_vir_address__;	\
	unsigned int __n_f_idx__ = __vir_addr__ >> 20;	\
	if((__m_baseaddress__[__n_f_idx__] & 0x3) == 0 )	\
	{	\
		__m_baseaddress__[__n_f_idx__] = ((((((__m_baseaddress__[__n_f_idx__] >> 10) \
			| (__phy_addr__ >> 10)) << 5) | __memmode__ ) << 1 | 0x1 ) << 4) | 0x01; \
	}	\
}

/**
 * COMMENTS:		Small Page Format

		Small page base address	AP3 AP2 AP1 AP0 C B 1 0
		[31:12] These bits form the corresponding bits of the physical
                        address.
		[11:4] 	Access permission bits. Domain access control on page 3-24
                    and Fault checking sequence on page 3-26 show how to
                    interpret the access permission bits.
		[3:2]  	These bits, C and B, indicate whether the area of memory
                    mapped by this page is treated as write-back cachable,
                    write-through cachable, noncached buffered, or noncached
                    nonbuffered.
		[1:0]  	These bits indicate the page size and validity and are
                    interpreted as shown in Table 3-8.

 */
#if	__K_MMU_USE_WRITEBACK__
	#define	K_MEM_MMU_WRITE_MODE_WB		1			// write-back enable
#else
	#define	K_MEM_MMU_WRITE_MODE_WB		0			// write-back disable
#endif

#if	__K_MMU_USE_BUFCACHE__
	#define	K_MEM_MMU_WRITE_MODE_B		1			// noncached, buffered
#else
	#define	K_MEM_MMU_WRITE_MODE_B		0			// noncached, nonbuffered
#endif

/*COMMENTS: In specification, AP can control 4 tiny page, but here, we treat them
 * 			as the same. so, bits 4~5,6~7,8~9,10~11 treated as same mode.
 */
/**
 * 填充第二级页表项
 * 		__firstleveltableaddress__ 必须是虚拟地址
 *		找到一级也表后，里面存放的是二级页表的物理地址，
 *	不能直接访问，需要将物理地址转换为虚拟地址!
 * memmode 为 AP 的mode
 */
#define	K_MEM_MMU_FILL_SMPT(__phy_addr__, __vir_addr__, __firstleveltable_vir_address__, __memmode__, __writemode__, __bufmode__)	\
{\
	unsigned int *__m_baseaddress__ = (unsigned int *)__firstleveltable_vir_address__;	\
	unsigned int __n_f_idx__ = __vir_addr__ >> 20;	\
	unsigned int __m_second_addr__ = K_MEM_MMU_SYS_PHY2VIR(__m_baseaddress__[__n_f_idx__] & 0xfffffc00);	\
	unsigned int *__m_second__ = (unsigned int *)__m_second_addr__;	\
	unsigned int __m_s_idx__ = (__vir_addr__ << 12) >> 24;	\
	__m_second__[__m_s_idx__] = 0;	\
	__m_second__[__m_s_idx__] =	((((((((__m_second__[__m_s_idx__]>>12)|(__phy_addr__ >> 12))<<2|__memmode__)<<2|__memmode__) \
				<<2|__memmode__)<<2|__memmode__)<<1|__writemode__)<<1|__bufmode__)<<2|0x02;	\
}

/**
 *  清空一个二级页表项(不能清空一级页表)
 */
#define	K_MEM_MMU_FREE_VIR(__vir_addr__, __task_fst_table_vir_addr__)	{	\
	unsigned int * __sec_phy_addr__ = (unsigned int *)\
	(K_MEM_MMU_SYS_PHY2VIR(__task_fst_table_vir_addr__[__vir_addr__>>20] & 0xfffff000));\
	__sec_phy_addr__[(__vir_addr__<<12)>>24] = 0;\
}

/**
 * 获得一个虚拟页面的物理地址.(__task_firsttable_addr__ 必须是虚拟地址.....)
 */
#define K_MEM_MMU_GET_PHYADDR(__vir_addr__, __phy_addr__, __task_firsttable_addr__)	{	\
	unsigned int *__m_fst_addr__ = (unsigned int *)__task_firsttable_addr__;	\
	unsigned int __f_idx__ = __vir_addr__ >> 20;	\
	unsigned int __s_idx__ = (__vir_addr__ << 12) >> 24;	\
	unsigned int *__m_sec_addr__ = (unsigned int *)(K_MEM_MMU_SYS_PHY2VIR(__m_fst_addr__[__f_idx__] & 0xfffffc00));	\
	__phy_addr__ = __m_sec_addr__[__s_idx__] & 0xfffff000;	\
}


/** current process/thread */
extern K_PProcess	g_p_cur_process;
extern K_PThread	g_p_cur_thread;

/*** RAM 的起始物理页面索引 */
extern unsigned int g_s_k_mem_phy_pagestart;

/** OS MMU First Level Page Table address **/
extern unsigned int k_mem_mmu_flpt_os;

/** OS MMU Second Level Page Tables start address **/
extern unsigned int k_mem_mmu_slpt_os;

/** I/O mapping address MMU Second Level Page Table start addres */
extern unsigned int k_mem_mmu_slpt_io_mapping;

/**
 * 									EXTERN FUNCTIONS
 */

/**----------------------------------------------------------------------------------
 * 										FUNCTIONS
 */

/**
 * @BRIEF	:	Initialize OS MMU
 * @PARAM	:	void
 * @RETURN	:	void
 * @NOTE	:	OS will map K_SYS_MEM_VIR_SIZE(defined in file basedefine.h) physical space
 * 			:	Physical address and virtual address are mapped One to One in fixed order!
 * 			: =>We should keep the order when we allocating pages for OS!
 */
void k_mem_mmu_initialize(void);

/**
 * @BRIEF	:	Allocate Virtual Pages
 * @PARAM	:	vm_mode			the mode used when allocating pages
 * @PARAM	:	page_count		count of pages will be allocated
 * @RETURN	:	virtual address of the first allocated virtual page
 * @NOTE	:	OS will map K_SYS_MEM_VIR_SIZE(defined in file basedefine.h) physical space
 * 			:	Physical address and virtual address are mapped One to One in fixed order!
 * 			: =>We should keep the order when we allocating pages for OS!
 * 			:
 * @INT		:	AUTO(disable and enable)
 */
void* k_mem_mmu_allocate(EN_K_MEM_PHY_USE_MODE vm_mode, unsigned int page_count);

/**
 * @BRIEF	:	Allocate First Level Page Table
 * @PARAM	:	void
 * @RETURN	:	physical address of FLPT
 * @NOTE	: =>This function will allocate new FLPT and fill it with
 * 			: =>System Space FLPT Record.
 * 			: =>This function return PHYSICAL ADDRESS!
 */
void* k_mem_mmu_allocate_flpt_return_physical_address(void);

/**
 * @BRIEF	:	Free First Level Page Table
 * @PARAM	:	phy_addr    physical address of FLPT
 * @RETURN	:	physical address of FLPT
 * @NOTE	: =>This function will free FLPT we don't need to free the
 * @WARNING : =>virtual space, because it's OS space, and can't change
 *          : =>any for ever.
 */
void k_mem_mmu_free_flpt_with_physical_address(unsigned int phy_addr);

/**
 * @BRIEF	:	Free Virtual Pages
 * @PARAM	:	vir_addr		virtual page will be freed
 * @RETURN	:	virtual address of the first allocated virtual page
 * @NOTE	: =>This function will just free one virtual page.If wanting more,
 * 			:	we should call this repeatedly.
 */
void k_mem_mmu_free(void *vir_addr);

/**
 * @BRIEF	:	Free Process user space
 * @PARAM	:	FLPT virtual address    address of process flpt
 */
void k_mem_mmu_freeprocessusrspace(unsigned int *addr_flpt);

#endif	// _K_MEM_SYS_MMU_H_
